package org.fixies.database.dbunit;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.operation.DatabaseOperation;
import org.fixies.spreadsheet.Sheet;
import org.fixies.spreadsheet.Unit;
import org.fixies.spreadsheet.converter.IDataSetConverter;
import org.fixies.utils.PropertyManager;
import org.fixies.utils.UnexpectedProblemException;
import org.fixies.utils.Validator;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * データベース操作に関するヘルパークラス<br/>
 *
 * @author Kawasaki
 * @version 1.0
 */
public class DBSupport {

    /**
     * データ登録時に行う操作（CLEAN INSERT）
     */
    private static DatabaseOperation dbOparation__ = DatabaseOperation.CLEAN_INSERT;

    /**
     * データソース
     */
    private DataSource ds_;

    /**
     * コンストラクタ
     */
    public DBSupport() {
    }

    /**
     * コンストラクタ
     *
     * @param ds データソース
     */
    public DBSupport(DataSource ds) {
        setDataSource(ds);
    }

    /**
     * データソースを設定する
     *
     * @param ds データソース
     */
    public final void setDataSource(DataSource ds) {
        Validator.notNull(ds);
        ds_ = ds;
    }

    /**
     * データベースの設定を行う。<br/>
     * DELETE - INSERT
     *
     * @param table 設定するテーブル
     */
    public void setUpDB(ITable table) {
        setUpDB(new DataSetImpl(table));
    }

    /**
     * データベースの設定を行う。<br/>
     *
     * @param unit 小表
     */
    public void setUpDB(Unit unit) {
        IDataSet dataSet = new DataSetImpl(unit);
        setUpDB(dataSet);
    }

    /**
     * データベースの設定を行う。<br/>
     *
     * @param sheet シート
     */
    public void setUpDB(Sheet sheet) {
        IDataSet dataSet = sheet.convert(new IDataSetConverter());
        setUpDB(dataSet);
    }

    /**
     * データベースの設定を行う。<br/>
     *
     * @param dataSet データセット
     */
    public void setUpDB(IDataSet dataSet) {
        Validator.notNull(dataSet);
        try {
            dbOparation__.execute(DBSupport.getIDatabaseConnection(ds_), dataSet);
        } catch (DatabaseUnitException e) {
            throw new UnexpectedProblemException(e);
        } catch (SQLException e) {
            throw new UnexpectedProblemException(e);
        }
    }

    /**
     * 引数で与えられたテーブルのデータが データベースに格納されていることを表明する。
     *
     * @param expected 期待値
     */
    public void assertEquals(ITable expected) {
        String tableName = expected.getTableMetaData().getTableName();
        ITable actual = getITable(tableName);
        DBAssertion.assertTablesEqual(expected, actual);
    }

    /**
     * 引数で与えられたデータセットのデータが データベースに格納されていることを表明する。
     *
     * @param expectedDataSet 期待値
     */
    public void assertContains(IDataSet expectedDataSet) {

        IDataSet actualDataSet = getIDataSet();
        DBAssertion.assertContains(expectedDataSet, actualDataSet);

    }

    /**
     * 引数で与えられたテーブルのデータが データベースに格納されていることを表明する。
     *
     * @param expected 期待値
     */
    public void assertContains(ITable expected) {
        String tableName = expected.getTableMetaData().getTableName();
        ITable actual = getITable(tableName);
        DBAssertion.assertContains(expected, actual);
    }

    private ITable getITable(String table) {
        try {
            return getIDataSet(table).getTable(table);
        } catch (DataSetException e) {
            throw new UnexpectedProblemException(e);
        }
    }


    private IDataSet getIDataSet(String... tables) {
        try {
            if (Validator.isNullOrEmpty(tables)) {
                return getConn().createDataSet();
            }
            return getConn().createDataSet(tables);
        } catch (SQLException e) {
            throw new UnexpectedProblemException(e);
        }
    }

    private IDatabaseConnection conn_ = null;

    private IDatabaseConnection getConn() {
        if (conn_ == null) {
            conn_ = getIDatabaseConnection(ds_);
        }
        return conn_;
    }

    /**
     * コネクションを取得する。
     *
     * @param ds データソース
     * @return コネクション
     */
    static IDatabaseConnection getIDatabaseConnection(DataSource ds) {
        try {
            String schemaName = getSchemaName();
            if (schemaName == null || schemaName.length() == 0) {
                return new DatabaseConnection(ds.getConnection());
            }
            return new DatabaseConnection(ds.getConnection(), schemaName);
        } catch (SQLException e) {
            throw new UnexpectedProblemException(e);
        }
    }

    /**
     * スキーマ名を取得する。
     *
     * @return スキーマ名
     */
    private static String getSchemaName() {
        return PropertyManager.getProperty("db.connection.schema");
    }
}
